package cn.z2huo.knowledge.concurrency.lock.readwritelock.reentrant;

import cn.hutool.core.thread.ThreadUtil;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * <p>ReentrantReadWriteLock 读写锁测试，锁降级，写锁降级为读锁。
 *
 * <p>{@link java.util.concurrent.locks.ReentrantReadWriteLock} 只支持写锁降级为读锁，而不支持读锁升级为写锁
 *
 * @author z2huo
 */
class ReadWriteLockLowerTest {

    /**
     * 共享数据
     */
    private final static Map<String, String> MAP = new HashMap<>();

    private final static ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();

    private final static Lock READ_LOCK = LOCK.readLock();

    private final static Lock WRITE_LOCK = LOCK.writeLock();

    /**
     * 对共享数据的写操作
     */
    public static Object put(String key, String value) {
        WRITE_LOCK.lock();
        try {
            System.out.println(LocalDateTime.now() + " get write lock");
            // 业务操作。。。
            ThreadUtil.sleep(1000);
            String put = MAP.put(key, value);

            System.out.println("尝试降级写锁为读锁");
            READ_LOCK.lock();
            System.out.println("写锁降级为读锁成功");

            return put;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            READ_LOCK.unlock();
            WRITE_LOCK.unlock();
        }
        return null;
    }

    /**
     * 对共享数据的读操作
     */
    public static Object get(String key) {
        READ_LOCK.lock();
        try {
            System.out.println(LocalDateTime.now() + " get read lock");

            ThreadUtil.sleep(1000);
            String value = MAP.get(key);

            System.out.println("尝试升级读锁为写锁");
            WRITE_LOCK.lock();
            System.out.println("读锁升级为写锁成功");

            return value;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            WRITE_LOCK.unlock();
            READ_LOCK.unlock();
        }
        return null;
    }

    public static void main(String[] args) {
        Runnable writeRunnable = () -> put("key", "value");
        Runnable readRunnable = () -> get("key");

        new Thread(writeRunnable).start();
        new Thread(readRunnable).start();
    }

}
